lib/commit: Use provided length when doing writes
authorColin Walters <walters@verbum.org>
Thu, 29 Jun 2017 19:58:05 +0000 (15:58 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Tue, 4 Jul 2017 16:15:11 +0000 (16:15 +0000)
This is prep for storage space checks, where we look at free
space after parsing the metadata, before we write anything.

We did length-limited writes in the fd-based input path, but not for the
`GInputStream` path which in practice is used for HTTP pulls.

Closes: #987
Approved by: jlebon

src/libostree/ostree-repo-commit.c

index 1ecbc178620ac57250fef019e4546b9d0d1996a1..9cc028e0308dc3149177095354bc118139a5347c 100644 (file)
@@ -481,10 +481,27 @@ create_regular_tmpfile_linkable_with_content (OstreeRepo *self,
     }
   else
     {
-      g_autoptr(GOutputStream) temp_out = g_unix_output_stream_new (tmpf.fd, FALSE);
-      if (g_output_stream_splice (temp_out, input, 0,
-                                  cancellable, error) < 0)
-        return FALSE;
+      /* We used to do a g_output_stream_splice(), but there are two issues with that:
+       *  - We want to honor the size provided, to avoid malicious content that says it's
+       *    e.g. 10 bytes but is actually gigabytes.
+       *  - Due to GLib bugs that pointlessly calls `poll()` on the output fd for every write
+       */
+      char buf[8192];
+      guint64 remaining = length;
+      while (remaining > 0)
+        {
+          const gssize bytes_read =
+            g_input_stream_read (input, buf, MIN (remaining, sizeof (buf)), cancellable, error);
+          if (bytes_read < 0)
+            return FALSE;
+          else if (G_UNLIKELY (bytes_read == 0 && remaining > 0))
+            return glnx_throw (error, "Unexpected EOF with %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " bytes remaining", remaining, length);
+          else if (bytes_read == 0)
+            break;
+          if (glnx_loop_write (tmpf.fd, buf, bytes_read) < 0)
+            return glnx_throw_errno_prefix (error, "write");
+          remaining -= bytes_read;
+        }
     }
 
   if (fchmod (tmpf.fd, 0644) < 0)